#include "SelectServer.hpp"

// 构造函数
SelectServer::SelectServer(uint16_t port, const std::string& ip)
    : _listenfd(-1), _port(port), _ip(ip), _fd_array(std::vector<int>(g_num, g_none_fd))
{  }

// 初始化函数
void SelectServer::init()
{
    // 获取套接字
    _listenfd = Sock::Socket();   
    if(_listenfd < 0)
    {
        logMessage(FATAL, "Socket fail, %d:%s\n", errno, strerror(errno));
        exit(2);
    }
    
    // 绑定端口号
    if(Sock::Bind(_listenfd, _ip, _port) < 0)
    {
        logMessage(FATAL, "Bind fail, %d:%s\n", errno, strerror(errno));
        exit(3);
    }

    // 设置监听状态
    if(Sock::Listen(_listenfd) < 0)
    {
        logMessage(FATAL, "Listen fail, %d:%s\n", errno, strerror(errno));
        exit(4);
    }

    // 协议定制：设置_fd_array[0]为listenfd
    _fd_array[0] = _listenfd;

    logMessage(NORMAL, "SelectServer init success!\n");
}


// Select服务器开始运行函数
void SelectServer::start()  
{
    // 服务器死循环运行
    while(1)
    {
        fd_set rfd;      // 关注的读取文件描述符
        FD_ZERO(&rfd);   // 文件描述符全部清0

        int maxfd = _listenfd;   // 关注的文件描述符最大值

        // 将_fd_array中的内容逐个添加到rfd中去
        for(const auto fd : _fd_array)
        {
            if(fd == g_none_fd) continue;
            FD_SET(fd, &rfd);
            if(fd > maxfd) maxfd = fd;
        }

        // 检查已经准备好的FD
        CheckFdArray();
        // printf("maxfd:%d\n", maxfd);

        // 设置select实现IO多路转接
        // 暂时设置为阻塞式
        int n = select(maxfd + 1, &rfd, nullptr, nullptr, nullptr);
        switch(n)
        {
        case -1:    // 出错的情况
            logMessage(ERROR, "select error, %d:%s\n", errno, strerror(errno));
            break;
        case 0:     // 超时，无就绪文件描述符
            logMessage(DEBUG, "time out, on fd prepared, n:%d\n", n);
            break;
        default:    // 有就绪文件描述符
            logMessage(NORMAL, "存在资源就绪的文件描述符, n:%d\n", n);
            SelectHandler(rfd);
            break;
        }
    }
}


// 析构函数
SelectServer::~SelectServer() 
{
    if(_listenfd < 0) close(_listenfd);
}


// fd_array检查打印函数
void SelectServer::CheckFdArray()
{
    std::cout << "fd_array[] " << std::flush;
    for(const int fd : _fd_array)
    {
        if(fd != g_none_fd) std::cout << fd << " ";
    }
    std::cout << std::endl;
}


// 就绪文件描述符处理函数
void SelectServer::SelectHandler(fd_set rfd)
{
    // std::cout << "Handler" << std::endl;
    // 开始轮询检测是否有就绪文件描述符
    for(const auto fd_check : _fd_array)
    {
        // 如果一个文件描述符就绪
        if(fd_check != g_none_fd && FD_ISSET(fd_check, &rfd))
        {
            if(fd_check == _listenfd)   
            {
                // 就绪的是listenfd，就接收连接
                Accepter();
            }
            else
            {
                // 是普通fd，就读取数据
                Reciver(fd_check);
            }
        }
    }
}


// 从网络中获取tcp链接函数
void SelectServer::Accepter()
{
    struct sockaddr_in cli_sock;
    memset(&cli_sock, 0, sizeof(cli_sock));
    socklen_t len = sizeof(cli_sock);

    int cli_fd = accept(_listenfd, (struct sockaddr *)&cli_sock, &len);
    if(cli_fd < 0)
    {
        logMessage(ERROR, "Accept Link Fail, %d:%s\n", errno, strerror(errno));
        return;
    }

    // 成功接收到新的fd，将其添加到_fd_array队列
    int index = 0;
    for(index = 0; index < g_num; ++index)
    {
        if(_fd_array[index] == g_none_fd)
        {
            _fd_array[index] = cli_fd;
            break;
        }
    }

    // 判断是否添加成功
    if(index == g_num)   // 可关注的文件描述符数量到达上限 
    {
        logMessage(ERROR, "The cared fd has already full, can not add new fd to fd_array, newfd:%d\n", cli_fd);
    }
    else
    {
        logMessage(NORMAL, "Add new fd success, _fd_array[%d]:%d\n", index, cli_fd);
    }
}


// 数据读取函数
void SelectServer::Reciver(int read_fd)
{
    char buffer[SIZE] = { 0 };

    // 不断从fd中获取消息并输出
    ssize_t n = read(read_fd, buffer, SIZE - 1);    
    if(n > 0)   
    {
        // 如果成功读取消息
        logMessage(NORMAL, "Recieve Client[%d] message success!", read_fd);
        buffer[n] = '\0';
        printf("Client[%d] say# %s\n", read_fd, buffer);

        std::string backMsg;
        backMsg += "echo# ";
        backMsg += buffer;

        ssize_t backN = send(read_fd, (void*)backMsg.c_str(), backMsg.size(), 0);  // 发回信息
        if(backN > 0)
        {
            logMessage(NORMAL, "send message back to Client[%d] success!\n", read_fd);
        }
        else
        {
            logMessage(ERROR, "send back to client[%d] fail, %d:%s\n", errno, strerror(errno));
        }

    }
    else if(n == 0)   // 客户端（写端）关闭 
    {
        logMessage(DEBUG, "Client[%d] close, erase this fd:%d\n", read_fd, read_fd);
        for(int index = 0; index < g_num; ++index)
        {
            if(_fd_array[index] == read_fd)
            {
                close(_fd_array[index]);
                _fd_array[index] = g_none_fd;
                break;
            }
        }
    }
    else
    {
        // 信息读取出错
        logMessage(ERROR, "Recievd message from Clienr[%d] error, %d:%s\n", read_fd, errno, strerror(errno));
    }
}